// exception.cc 
//	Entry point into the Nachos kernel from user programs.
//	There are two kinds of things that can cause control to
//	transfer back to here from user code:
//
//	syscall -- The user code explicitly requests to call a procedure
//	in the Nachos kernel.  Right now, the only function we support is
//	"Halt".
//
//	exceptions -- The user code does something that the CPU can't handle.
//	For instance, accessing memory that doesn't exist, arithmetic errors,
//	etc.  
//
//	Interrupts (which can also cause control to transfer from user
//	code into the Nachos kernel) are handled elsewhere.
//
// For now, this only handles the Halt() system call.
// Everything else core dumps.
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved.  See copyright.h for copyright notice and limitation 
// of liability and disclaimer of warranty provisions.

#include "copyright.h"
#include "system.h"
#include "syscall.h"
#ifdef NETWORK
#include "network.h"
#include "post.h"
#endif

#include <stdio.h>
#include <iostream>

using namespace std;
extern "C" { int bzero(char *, int); };

#ifdef NETWORK

// location of the server
int serverMachineId = rand()%NUMBER_OF_SERVERS;
int serverId        = 0;

// sentinal value
int BAD_VALUE_SENTINAL = -99999;

// Packet and Mail headers (out and in)
// Mail Headers (out and in)
PacketHeader kernelOutPktHdr, kernelInPktHdr;
MailHeader kernelOutMailHdr, kernelInMailHdr;

// data being received for response
char kernelMailBuffer[MaxMailSize];

// data being sent
char kernelSendData[MaxMailSize];

/* Helper method to save space */
void
PostSuccess(bool good) {
    if ( !good ) {
        printf("The postOffice Send failed. You may not have the server Nachos running. Terminating Nachos.\n");
        interrupt->Halt();
    }
}

#endif

int copyin(unsigned int vaddr, int len, char *buf) {
    // Copy len bytes from the current thread's virtual address vaddr.
    // Return the number of bytes so read, or -1 if an error occors.
    // Errors can generally mean a bad virtual address was passed in.
    bool result;
    int n=0;			// The number of bytes copied in
    int *paddr = new int;
	
    while ( n >= 0 && n < len) {
		result = machine->ReadMem( vaddr, 1, paddr );
		while(!result) // FALL 09 CHANGES
		{
   			result = machine->ReadMem( vaddr, 1, paddr ); // FALL 09 CHANGES: TO HANDLE PAGE FAULT IN THE ReadMem SYS CALL
		}	
		
		buf[n++] = *paddr;
		
		if ( !result ) {
			//translation failed
			return -1;
		}
		
		vaddr++;
    }
	
    delete paddr;
    return len;
}

int copyinInt(unsigned int vaddr, int len, int *buf) {
    // Copy len bytes from the current thread's virtual address vaddr.
    // Return the number of bytes so read, or -1 if an error occors.
    // Errors can generally mean a bad virtual address was passed in.
	// just like copyin() but for integer arrays instead of character arrays
	bool result;
    int n=0;			// The number of bytes copied in
    int *paddr = new int;
	
    while ( n >= 0 && n < len) {
		result = machine->ReadMem( vaddr, 4, paddr );
		while(!result) // FALL 09 CHANGES
		{
   			result = machine->ReadMem( vaddr, 4, paddr ); // FALL 09 CHANGES: TO HANDLE PAGE FAULT IN THE ReadMem SYS CALL
		}	
		
		buf[n++] = *paddr;
		
		if ( !result ) {
			//translation failed
			return -1;
		}
		
		vaddr+= 4;
    }
	
    delete paddr;
	return len;
}

int copyout(unsigned int vaddr, int len, char *buf) {
    // Copy len bytes to the current thread's virtual address vaddr.
    // Return the number of bytes so written, or -1 if an error
    // occors.  Errors can generally mean a bad virtual address was
    // passed in.
    bool result;
    int n=0;			// The number of bytes copied in
	
    while ( n >= 0 && n < len) {
		// Note that we check every byte's address
		result = machine->WriteMem( vaddr, 1, (int)(buf[n++]) );
		
		if ( !result ) {
			//translation failed
			return -1;
		}
		
		vaddr++;
    }
	
    return n;
}

void Create_Syscall(unsigned int vaddr, int len) {
    // Create the file with the name in the user buffer pointed to by
    // vaddr.  The file name is at most MAXFILENAME chars long.  No
    // way to return errors, though...
    char *buf = new char[len+1];	// Kernel buffer to put the name in
	
    if (!buf) return;
	
    if( copyin(vaddr,len,buf) == -1 ) {
		printf("%s","Bad pointer passed to Create\n");
		delete buf;
		return;
    }
	
    buf[len]='\0';
	
    fileSystem->Create(buf,0);
    delete[] buf;
    return;
}

int Open_Syscall(unsigned int vaddr, int len) {
    // Open the file with the name in the user buffer pointed to by
    // vaddr.  The file name is at most MAXFILENAME chars long.  If
    // the file is opened successfully, it is put in the address
    // space's file table and an id returned that can find the file
    // later.  If there are any errors, -1 is returned.
    char *buf = new char[len+1];	// Kernel buffer to put the name in
    OpenFile *f;			// The new open file
    int id;				// The openfile id
	
    if (!buf) {
		printf("%s","Can't allocate kernel buffer in Open\n");
		return -1;
    }
	
    if( copyin(vaddr,len,buf) == -1 ) {
		printf("%s","Bad pointer passed to Open\n");
		delete[] buf;
		return -1;
    }
	
    buf[len]='\0';
	
    f = fileSystem->Open(buf);
    delete[] buf;
	
    if ( f ) {
		if ((id = currentThread->space->fileTable.Put(f)) == -1 )
			delete f;
		return id;
    }
    else
		return -1;
}

void Write_Syscall(unsigned int vaddr, int len, int id) {
    // Write the buffer to the given disk file.  If ConsoleOutput is
    // the fileID, data goes to the synchronized console instead.  If
    // a Write arrives for the synchronized Console, and no such
    // console exists, create one. For disk files, the file is looked
    // up in the current address space's open file table and used as
    // the target of the write.
    
    char *buf;		// Kernel buffer for output
    OpenFile *f;	// Open file for output
	
    if ( id == ConsoleInput) return;
    
    if ( !(buf = new char[len]) ) {
		printf("%s","Error allocating kernel buffer for write!\n");
		return;
    } else {
        if ( copyin(vaddr,len,buf) == -1 ) {
			printf("%s","Bad pointer passed to to write: data not written\n");
			delete[] buf;
			return;
		}
    }
	
    if ( id == ConsoleOutput) 
	{
		int foundDec = 0;
		for (int ii=0; ii<len; ii++) 
		{
			printf("%c",buf[ii]);
		}
    } else {
		if ( (f = (OpenFile *) currentThread->space->fileTable.Get(id)) ) {
			f->Write(buf, len);
		} else {
			printf("%s","Bad OpenFileId passed to Write\n");
			len = -1;
		}
    }
	
    delete[] buf;
}

int Read_Syscall(unsigned int vaddr, int len, int id) {
    // Write the buffer to the given disk file.  If ConsoleOutput is
    // the fileID, data goes to the synchronized console instead.  If
    // a Write arrives for the synchronized Console, and no such
    // console exists, create one.    We reuse len as the number of bytes
    // read, which is an unnessecary savings of space.
    char *buf;		// Kernel buffer for input
    OpenFile *f;	// Open file for output
	
    if ( id == ConsoleOutput) return -1;
    
    if ( !(buf = new char[len]) ) {
		printf("%s","Error allocating kernel buffer in Read\n");
		return -1;
    }
	
    if ( id == ConsoleInput) {
		//Reading from the keyboard
		scanf("%s", buf);
		
		if ( copyout(vaddr, len, buf) == -1 ) {
			printf("%s","Bad pointer passed to Read: data not copied\n");
		}
    } else {
		if ( (f = (OpenFile *) currentThread->space->fileTable.Get(id)) ) {
			len = f->Read(buf, len);
			if ( len > 0 ) {
				//Read something from the file. Put into user's address space
				if ( copyout(vaddr, len, buf) == -1 ) {
					printf("%s","Bad pointer passed to Read: data not copied\n");
				}
			}
		} else {
			printf("%s","Bad OpenFileId passed to Read\n");
			len = -1;
		}
    }
	
    delete[] buf;
    return len;
}

void Close_Syscall(int fd) {
    // Close the file associated with id fd.  No error reporting.
    OpenFile *f = (OpenFile *) currentThread->space->fileTable.Remove(fd);
	
    if ( f ) {
		delete f;
    } else {
		printf("%s","Tried to close an unopen file\n");
    }
}

// Performs the Yield
void Yield_Syscall()
{
	// call current thread yield
	DEBUG('a',"Yield system call was made!\n");
	currentThread->Yield();
}

#ifdef NETWORK
int CreateLockNetwork(char* lockName) {
    int myMailBox = currentThread->getID();

    kernelOutPktHdr.to = serverMachineId;           //servers machine ID
    kernelOutMailHdr.to = serverId;                 // server's mailbox
    kernelOutMailHdr.from = myMailBox;              // my mailbox

    sprintf(kernelSendData, "%i,%s", SC_CreateLock, lockName);
    kernelOutMailHdr.length = strlen(kernelSendData) + 1;

    // send the package
    PostSuccess(postOffice->Send(kernelOutPktHdr, kernelOutMailHdr, kernelSendData));

    // receive the response
    postOffice->Receive(myMailBox, &kernelInPktHdr, &kernelInMailHdr, kernelMailBuffer);
    char* token = strtok(kernelMailBuffer, ",");

    if(token != NULL) {
        if(atoi(token) == 1) {
            token = strtok(NULL, ",");
            if(token) {
                return atoi(token);
            }
        } else {
            printf("ERROR: I got a response but didn't Create the lock.\n");
        }
    } else {
        printf("ERROR: Token was null.\n");
    }

    return -1;
}
#endif

// Creates a lock for the user and returns an int of the
// index for the lock in KernelLocks array
int CreateLock_Syscall(unsigned int addrToLockName, int charLength)
{
	// acquire the kernelLockTable lock for mut ex
	kernelLockTableLock->Acquire();
	// do error checking here
	if(charLength < 1)
	{
		printf("ERROR: Passing in a negative character length.\n");
		kernelLockTableLock->Release();
		return -1;
	}
	if(charLength > 100)
	{
		printf("ERROR: Character length cannot be greater than 100 characters.\n");
		kernelLockTableLock->Release();
		return -1;
	}
	/* if the max amount of locks has been reached */
	if(amtOfLocks == MAX_LOCKS || amtOfLocks < 0)
	{
		printf("ERROR: Lock could not be created because the max. number of locks has been reached.\n");
		kernelLockTableLock->Release();
		return -1;
	}
	/* insert lock into the array of KernelLock and do some housekeeping */
	// get the char* for the name of the lock
	char* lockName = new char[charLength+1];
	if(!lockName)
	{
		kernelLockTableLock->Release();
		return -1;
	}
	// check if the name addr passed in is a bad virtual addr
	if(copyin(addrToLockName, charLength, lockName) == -1)
	{
		printf("ERROR: Bad pointer for lock name.\n");
		delete lockName;
		kernelLockTableLock->Release();
		return -1;
	}
	lockName[charLength] = '\0';
	
    #ifdef NETWORK
        return CreateLockNetwork(lockName);
        kernelLockTableLock->Release();
    #else

        // New Lock struct and set members
        KernelLock* newLock = new KernelLock();
        newLock->lock = new Lock(lockName);
        newLock->space = currentThread->space;
        newLock->isDeleted = false;
        newLock->isToBeDeleted = false;

        /* look for a position to insert the lock */
        for(int i=0; i < MAX_LOCKS; i++)
        {
            // if the lock was deleted then insert lock here
            if(!kernelLockTable[i] || kernelLockTable[i]->isDeleted)
            {
                //printf("Creating lock: %s. Index: %d.\n", lockName, i);
                kernelLockTable[i] = newLock;
                amtOfLocks++;
                // release the kernelLockTable lock for mut ex
                kernelLockTableLock->Release();
                return i;
            }
        }
        return -1;

    #endif
}

#ifdef NETWORK
int CreateCVNetwork(char* cvName) {
    int myMailBox = currentThread->getID();

    kernelOutPktHdr.to = serverMachineId;           //servers machine ID
    kernelOutMailHdr.to = serverId;                 // server's mailbox
    kernelOutMailHdr.from = myMailBox;              // my mailbox

    sprintf(kernelSendData, "%i,%s", SC_CreateCV, cvName);
    kernelOutMailHdr.length = strlen(kernelSendData) + 1;

    // send the package
    PostSuccess(postOffice->Send(kernelOutPktHdr, kernelOutMailHdr, kernelSendData));

    // receive the response
    postOffice->Receive(myMailBox, &kernelInPktHdr, &kernelInMailHdr, kernelMailBuffer);
    char* token = strtok(kernelMailBuffer, ",");

    if(token != NULL) {
        if(atoi(token) == 1) {
            token = strtok(NULL, ",");
            if(token != NULL) {
                return atoi(token);
            }
        } else {
            printf("ERROR: I got a response but didn't Create the CV.\n");
        }
    } else {
        printf("ERROR: Token was null.\n");
    }

    return -1;
}
#endif

// Creates a condition for the user and returns an int of the 
// index for the condition in KernelConditions array
int CreateCV_Syscall(unsigned int addrToCVName, int charLength)
{

    kernelConditionTableLock->Acquire();
    // do error checking here
    if(charLength < 1)
    {
        printf("ERROR: Passing in a negative character length.\n");
        kernelLockTableLock->Release();
        return -1;
    }
    if(charLength > 100)
    {
        printf("ERROR: Character length cannot be greater than 100 characters.\n");
        kernelLockTableLock->Release();
        return -1;
    }
    /* if the max amount of conditions has been reached	 */
    if(amtOfCVs == MAX_CONDITIONS || amtOfCVs < 0)
    {
        printf("ERROR: Condition could not be created because the max. number of conditions has been reached.\n");
        kernelConditionTableLock->Release();
        return -1;
    }
    // insert condition into the array of KernelCondition and do some housekeeping
    // get the char* for the name of the lock
    char* cvName = new char[charLength+1];
    // check if pointing to null space
    if(!cvName)
    {
        kernelConditionTableLock->Release();
        return -1;
    }
    // check if the name addr passed in is not pointing to anything
    if(copyin(addrToCVName, charLength, cvName) == -1)
    {
        printf("ERROR: Bad pointer for condition name.\n");
        delete cvName;
        kernelConditionTableLock->Release();
        return -1;
    }
    cvName[charLength] = '\0';

    #ifdef NETWORK
        return CreateCVNetwork(cvName);
        kernelConditionTableLock->Release();
    #else

        // New KernelCondition struct and set members
        KernelCondition* newCV = new KernelCondition();
        newCV->condition = new Condition(cvName);
        newCV->space = currentThread->space;
        newCV->isDeleted = false;
        newCV->isToBeDeleted = false;

        /* look through the array and find a location for this cv */
        for(int i=0; i < MAX_CONDITIONS; i++)
        {
            if(!kernelConditionTable[i] || kernelConditionTable[i]->isDeleted)
            {
                kernelConditionTable[i] = newCV;
                amtOfCVs++;
                //printf("Creating condition: %s. Index: %d.\n", cvName, i);
                kernelConditionTableLock->Release();
                return i;
            }
        }
        return -1;
	
    #endif
}

#ifdef NETWORK
int SignalCVNetwork(int cvNumber, int lockNumber) {
    int myMailBox = currentThread->getID();

    kernelOutPktHdr.to = serverMachineId;           //servers machine ID
    kernelOutMailHdr.to = serverId;                 // server's mailbox
    kernelOutMailHdr.from = myMailBox;              // my mailbox

    sprintf(kernelSendData, "%i,%i,%i", SC_Signal, cvNumber, lockNumber);
    kernelOutMailHdr.length = strlen(kernelSendData) + 1;

    // send the package
    PostSuccess(postOffice->Send(kernelOutPktHdr, kernelOutMailHdr, kernelSendData));

    // receive the response
    postOffice->Receive(myMailBox, &kernelInPktHdr, &kernelInMailHdr, kernelMailBuffer);
    char* token = strtok(kernelMailBuffer, ",");

    if(token != NULL) {
        int retVal = atoi(token);
        if(retVal == 1) {
            // we signaled sucsessfully
            return retVal;
        } else {
            printf("ERROR: I got a response but did not signal properly.\n");
        }
    } else {
        printf("ERROR: Token was null.\n");
    }

    return -1;
}
#endif

// Signals the specified Condition
int SignalCV_Syscall(int cvNumber, int lockNumber)
{
    #ifdef NETWORK
        return SignalCVNetwork(cvNumber, lockNumber);
    #else

        kernelConditionTableLock->Acquire();
        kernelLockTableLock->Acquire();
        /* user prog trying to request a ConditionVar number out of range */
        if(cvNumber < 0 || cvNumber > MAX_CONDITIONS-1)
        {
            //printf("ERROR: Condition var number is out of bounds. Max condition vars allowed %d.\n", MAX_CONDITIONS);
            kernelLockTableLock->Release();
            kernelConditionTableLock->Release();
            return -1;
        }
        /* user prog trying to signal a lock that is out of range */
        if(lockNumber < 0 || lockNumber > MAX_LOCKS-1)
        {
            printf("ERROR: Lock number is out of bounds. Max lock allowed %d\n.", MAX_LOCKS);
            kernelLockTableLock->Release();
            kernelConditionTableLock->Release();
            return -1;
        }
        // check address space
        if(kernelConditionTable[cvNumber]->space != currentThread->space ||
           kernelLockTable[lockNumber]->space != currentThread->space)
        {
            printf("ERROR: Lock or condition var belongs to a different address space than the process.\n");
            kernelLockTableLock->Release();
            kernelConditionTableLock->Release();
            return -1;
        }
        /* check if the cv is deleted */
        if(kernelConditionTable[cvNumber]->isDeleted)
        {
            printf("ERROR: %s was destroyed. Cannot be signalled.\n", kernelConditionTable[cvNumber]->condition->getName());
            kernelLockTableLock->Release();
            kernelConditionTableLock->Release();
            return -1;
        }
        /* check if the lock is deleted */
        if(kernelLockTable[lockNumber]->isDeleted)
        {
            printf("ERROR: %s was destroyed. Cannot be signalled.\n", kernelLockTable[lockNumber]->lock->getName());
            kernelLockTableLock->Release();
            kernelConditionTableLock->Release();
            return -1;
        }
        //printf("%s is signalling on %s.\n", currentThread->getName(), kernelConditionTable[cvNumber]->condition->getName());
        kernelConditionTable[cvNumber]->condition->Signal(kernelLockTable[lockNumber]->lock);
        kernelLockTableLock->Release();
        kernelConditionTableLock->Release();
        return 1;

    #endif
}

#ifdef NETWORK
int AcquireLockNetwork(int lockNumber) {
    int myMailBox = currentThread->getID();

    kernelOutPktHdr.to = serverMachineId;           //servers machine ID
    kernelOutMailHdr.to = serverId;                 // server's mailbox
    kernelOutMailHdr.from = myMailBox;              // my mailbox

    sprintf(kernelSendData, "%i,%i", SC_Acquire, lockNumber);
    kernelOutMailHdr.length = strlen(kernelSendData) + 1;

    // send the package
    PostSuccess(postOffice->Send(kernelOutPktHdr, kernelOutMailHdr, kernelSendData));

    // receive the response
    postOffice->Receive(myMailBox, &kernelInPktHdr, &kernelInMailHdr, kernelMailBuffer);
    char* token = strtok(kernelMailBuffer, ",");

    if(token != NULL) {
        if(atoi(token) == 1) {
            token = strtok(NULL, ",");
            if(token != NULL) {
                // happy case
                return 1;
            }
        } else {
            printf("ERROR: I got a response but don't have the lock.\n");
        }
    } else {
        printf("ERROR: Token was null.\n");
    }

    return -1;
}
#endif

// Will attempt to acquire the specified lock from the kernel lock table
int AcquireLock_Syscall(int lockNumber)
{
    #ifdef NETWORK
        return AcquireLockNetwork(lockNumber);
    #else

	// acquire the kernelLockTable lock for mut ex
	kernelLockTableLock->Acquire();
	//printf("%s TRY ACQUIRE for Lock# %d.\n", currentThread->getName(), lockNumber);
	// trying to acquire a lock number that is out of bounds of table
	if(lockNumber < 0 || lockNumber > MAX_LOCKS-1)
	{
		printf("ERROR: Lock number is out of bounds. Max locks allowed %d.\n", MAX_LOCKS);
		kernelLockTableLock->Release();
		return -1;
	}
	/* check if the lock is deleted */
	if(kernelLockTable[lockNumber]->isDeleted)
	{
		printf("ERROR: %s was destroyed. Cannot be acquired.\n", kernelLockTable[lockNumber]->lock->getName());
		kernelLockTableLock->Release();
		return -1;
	}
	/* check if the currentThread is trying to access an addrspace that is not 
	 * part of this process
	 */
	if(kernelLockTable[lockNumber]->space != currentThread->space)
	{
		printf("ERROR: Lock is not owned by this thread's process.\n");
		kernelLockTableLock->Release();
		return -1;
	}	
	// done with error checking, go ahead and try to acquire the lock
	// release the kernelLockTable lock for mut ex
	
	kernelLockTableLock->Release();
	processTableLock->Acquire();
	processTable[currentThread->space->GetProcessId()]->activeThreadCount--;
	processTableLock->Release();

	kernelLockTable[lockNumber]->lock->Acquire();

	processTableLock->Acquire();
	processTable[currentThread->space->GetProcessId()]->activeThreadCount++;
	processTableLock->Release();
	//printf("%s ACQUIRED lock: %s. Index: %d.\n", currentThread->getName(), kernelLockTable[lockNumber]->lock->getName(), lockNumber);
	return 1;

    #endif
}

#ifdef NETWORK
int DestroyLockNetwork(int lockNumber) {
    int myMailBox = currentThread->getID();

    kernelOutPktHdr.to = serverMachineId;           //servers machine ID
    kernelOutMailHdr.to = serverId;                 // server's mailbox
    kernelOutMailHdr.from = myMailBox;              // my mailbox

    sprintf(kernelSendData, "%i,%i", SC_DestroyLock, lockNumber);
    kernelOutMailHdr.length = strlen(kernelSendData) + 1;

    // send the package
    PostSuccess(postOffice->Send(kernelOutPktHdr, kernelOutMailHdr, kernelSendData));

    // receive the response
    postOffice->Receive(myMailBox, &kernelInPktHdr, &kernelInMailHdr, kernelMailBuffer);
    char* token = strtok(kernelMailBuffer, ",");

    if(token != NULL) {
        int retVal = atoi(token);
        if(atoi(token) == 1) {
            return retVal;
        } else {
            printf("ERROR: I got a response but didn't Destroy the lock.\n");
        }
    } else {
        printf("ERROR: Token was null.\n");
    }

    return -1;
}
#endif

// Will attempt to destroy the lock specified
int DestroyLock_Syscall(int lockNumber)
{
    #ifdef NETWORK
        return DestroyLockNetwork(lockNumber);
    #else

        // acquire the kernelLockTAble lock for mutex
        kernelLockTableLock->Acquire();
        // trying to acquire a lock number that is out of bounds of table
        if(lockNumber < 0 || lockNumber > MAX_LOCKS-1)
        {
            printf("ERROR: Lock number is out of bounds. Max locks allowed %d.\n", MAX_LOCKS);
            kernelLockTableLock->Release();
            return -1;
        }
        if(!kernelLockTable[lockNumber])
        {
            printf("ERROR: Lock was never created.\n");
            kernelLockTableLock->Release();
            return -1;
        }
        // trying to destroy a lock that this thread's process does not own
        if(kernelLockTable[lockNumber]->space != currentThread->space)
        {
            printf("ERROR: Lock is not part of process's addr space.\n");
            kernelLockTableLock->Release();
            return -1;
        }
        // if the lock is in use then mark it as TO BE DELETED
        if(!kernelLockTable[lockNumber]->lock->isFree())
        {
            printf("Cannot destroy the lock. %s is BUSY. Setting lock to be deleted on release.\n", kernelLockTable[lockNumber]->lock->getName());
            kernelLockTable[lockNumber]->isToBeDeleted = true;
            kernelLockTableLock->Release();
            return 1;
        }
        // if the lock was already destroyed then give error msg
        if(kernelLockTable[lockNumber]->isDeleted)
        {
            printf("ERROR: %s was already destroyed.\n", kernelLockTable[lockNumber]->lock->getName());
            kernelLockTableLock->Release();
            return -1;
        }
        // if threads are waiting on the lock mark it for deletion
        if(kernelLockTable[lockNumber]->lock->hasWaitingThreads())
        {
            printf("ERROR: Cannot delete %s. Has threads in waiting queue.\n", kernelLockTable[lockNumber]->lock->getName());
            kernelLockTableLock->Release();
            return -1;
        }
        // destroy the lock, set it to isDeleted
        //printf("Destroying lock %s. Index: %d.\n", kernelLockTable[lockNumber]->lock->getName(), lockNumber);
        kernelLockTable[lockNumber]->lock->~Lock();
        kernelLockTable[lockNumber]->isDeleted = true;
        amtOfLocks--;
        kernelLockTableLock->Release();
        return 1;

    #endif
}

#ifdef NETWORK
int ReleaseLockNetwork(int lockNumber) {
    int myMailBox = currentThread->getID();

    kernelOutPktHdr.to = serverMachineId;           //servers machine ID
    kernelOutMailHdr.to = serverId;                 // server's mailbox
    kernelOutMailHdr.from = myMailBox;              // my mailbox

    sprintf(kernelSendData, "%i,%i", SC_Release, lockNumber);
    kernelOutMailHdr.length = strlen(kernelSendData) + 1;

    // send the package
    PostSuccess(postOffice->Send(kernelOutPktHdr, kernelOutMailHdr, kernelSendData));

    // receive the response
    postOffice->Receive(myMailBox, &kernelInPktHdr, &kernelInMailHdr, kernelMailBuffer);
    char* token = strtok(kernelMailBuffer, ",");

    if(token != NULL) {
        if(atoi(token) == 1) {
            token = strtok(NULL, ",");
            if(token != NULL) {
                // happy case
                return 1;
            }
        } else {
            printf("ERROR: I got a response but did not Release the Lock.\n");
        }
    } else {
        printf("ERROR: Token was null.\n");
    }

    return -1;
}
#endif

// Will attempt to release the specified lock
int ReleaseLock_Syscall(int lockNumber)
{
    #ifdef NETWORK
        return ReleaseLockNetwork(lockNumber);
    #else

        // acquire the kernelLockTAble lock for mutex
        kernelLockTableLock->Acquire();
        // trying to acquire a lock number that is out of bounds of table
        if(lockNumber < 0 || lockNumber > MAX_LOCKS-1)
        {
            printf("ERROR: Lock number is out of bounds. Max locks allowed %d.\n", MAX_LOCKS);
            kernelLockTableLock->Release();
            return -1;
        }
        // trying to release a lock that this thread's process does not own
        if(kernelLockTable[lockNumber]->space != currentThread->space)
        {
            printf("ERROR: Lock is not part of process's addr space.\n");
            kernelLockTableLock->Release();
            return -1;
        }
        // check if lock was alredy destroyed
        if(kernelLockTable[lockNumber]->isDeleted)
        {
            printf("ERROR: %s was destroyed cannot be released.\n", kernelLockTable[lockNumber]->lock->getName());
            kernelLockTableLock->Release();
            return -1;
        }
        // check if the lock was never acquired
        if(kernelLockTable[lockNumber]->lock->isFree())
        {
            printf("ERROR: %s was never acquired (thread: %s).\n", kernelLockTable[lockNumber]->lock->getName(),
				currentThread->getName());
            kernelLockTableLock->Release();
            return -1;
        }
        //printf("Releasing lock: %s. Index: %d.\n", kernelLockTable[lockNumber]->lock->getName(), lockNumber);
        kernelLockTable[lockNumber]->lock->Release();
        // check if the lock was marked to be deleted
        if(kernelLockTable[lockNumber]->isToBeDeleted &&
           kernelLockTable[lockNumber]->lock->isFree())
        {
            // delete the lock
            //printf("%s is scheduled to be destroyed. Destroying lock.\n", kernelLockTable[lockNumber]->lock->getName());
            kernelLockTable[lockNumber]->lock->~Lock();
            amtOfLocks--;
            kernelLockTable[lockNumber]->isDeleted = true;
            kernelLockTableLock->Release();
            return 1;
        }
        kernelLockTableLock->Release();
        return 1;

    #endif
}

#ifdef NETWORK
int WaitCVNetwork(int cvNumber, int lockToWaitOn) {
    int myMailBox = currentThread->getID();

    kernelOutPktHdr.to = serverMachineId;           //servers machine ID
    kernelOutMailHdr.to = serverId;                 // server's mailbox
    kernelOutMailHdr.from = myMailBox;              // my mailbox

    sprintf(kernelSendData, "%i,%i,%i", SC_Wait, cvNumber, lockToWaitOn);
    kernelOutMailHdr.length = strlen(kernelSendData) + 1;

    // send the package
    PostSuccess(postOffice->Send(kernelOutPktHdr, kernelOutMailHdr, kernelSendData));

    // receive the response
    postOffice->Receive(myMailBox, &kernelInPktHdr, &kernelInMailHdr, kernelMailBuffer);
    char* token = strtok(kernelMailBuffer, ",");

    if(token != NULL) {
        int retVal = atoi(token);
        if(atoi(token) == 1) {
            return retVal;
        } else {
            printf("ERROR: I got a response but don't have the lock.\n");
        }
    } else {
        printf("ERROR: Token was null.\n");
    }

    return -1;
}
#endif

// Waits on the specified lock with the given cv
int WaitCV_Syscall(int cvNumber, int lockToWaitOn)
{
    #ifdef NETWORK
        return WaitCVNetwork(cvNumber, lockToWaitOn);
    #else

        kernelConditionTableLock->Acquire();
        kernelLockTableLock->Acquire();
        /* user prog trying to request a ConditionVar number out of range */
        if(cvNumber < 0 || cvNumber > MAX_CONDITIONS-1)
        {
            //printf("ERROR: Condition var number is out of bounds. Max condition vars allowed %d\n.", MAX_CONDITIONS);
            kernelLockTableLock->Release();
            kernelConditionTableLock->Release();
            return -1;
        }
        /* user prog trying to signal a lock that is out of range */
        if(lockToWaitOn < 0 || lockToWaitOn > MAX_LOCKS-1)
        {
            printf("ERROR: Lock number is out of bounds. Max lock allowed %d\n.", MAX_LOCKS);
            kernelLockTableLock->Release();
            kernelConditionTableLock->Release();
            return -1;
        }
        // check address space
        if(kernelConditionTable[cvNumber]->space != currentThread->space ||
           kernelLockTable[lockToWaitOn]->space != currentThread->space)
        {
            printf("ERROR: Lock or condition var belongs to a different address space than the process.\n");
            kernelLockTableLock->Release();
            kernelConditionTableLock->Release();
            return -1;
        }
        /* check if the cv is deleted */
        if(kernelConditionTable[cvNumber]->isDeleted)
        {
            printf("ERROR: %s was destroyed. Cannot be signalled.\n", kernelConditionTable[cvNumber]->condition->getName());
            kernelLockTableLock->Release();
            kernelConditionTableLock->Release();
            return -1;
        }
        /* check if the lock is deleted */
        if(kernelLockTable[lockToWaitOn]->isDeleted)
        {
            printf("ERROR: %s was destroyed. Cannot be signalled.\n", kernelLockTable[lockToWaitOn]->lock->getName());
            kernelLockTableLock->Release();
            kernelConditionTableLock->Release();
            return -1;
        }
        // decrease active thread count
        processTableLock->Acquire();
        processTable[currentThread->space->GetProcessId()]->activeThreadCount--;
        processTableLock->Release();
        kernelLockTableLock->Release();
        kernelConditionTableLock->Release();
        //printf("%s is waiting on %s.\n", currentThread->getName(), kernelConditionTable[cvNumber]->condition->getName());
        kernelConditionTable[cvNumber]->condition->Wait(kernelLockTable[lockToWaitOn]->lock);
        kernelConditionTableLock->Acquire();
        //printf("Woken up\n");
        // check if the cv was scheduled to be deleted
        if(kernelConditionTable[cvNumber]->isToBeDeleted)
        {
            //printf("Gonna be deleted\n");
            kernelConditionTable[cvNumber]->isToBeDeleted = false;
            kernelConditionTable[cvNumber]->isDeleted = true;
            kernelConditionTable[cvNumber]->condition->~Condition();
            amtOfCVs--;
            //printf("Deleted\n");
        }

        processTableLock->Acquire();
        processTable[currentThread->space->GetProcessId()]->activeThreadCount++;
        processTableLock->Release();
        kernelConditionTableLock->Release();
        return 1;
	
    #endif
}

#ifdef NETWORK
int BroadcastCVNetwork(int cvNumber, int lockToBroadcast) {
    int myMailBox = currentThread->getID();

    kernelOutPktHdr.to = serverMachineId;           //servers machine ID
    kernelOutMailHdr.to = serverId;                 // server's mailbox
    kernelOutMailHdr.from = myMailBox;              // my mailbox

    sprintf(kernelSendData, "%i,%i,%i", SC_Broadcast, cvNumber, lockToBroadcast);
    kernelOutMailHdr.length = strlen(kernelSendData) + 1;

    // send the package
    PostSuccess(postOffice->Send(kernelOutPktHdr, kernelOutMailHdr, kernelSendData));

    // receive the response
    postOffice->Receive(myMailBox, &kernelInPktHdr, &kernelInMailHdr, kernelMailBuffer);
    char* token = strtok(kernelMailBuffer, ",");

    if(token != NULL) {
        int retVal = atoi(token);
        if(atoi(token) == 1) {
            return retVal;
        } else {
            printf("ERROR: I got a response but did not broadcast properly.\n");
        }
    } else {
        printf("ERROR: Token was null.\n");
    }

    return -1;
}
#endif

int BroadcastCV_Syscall(int cvNumber, int lockToBroadcast)
{
    #ifdef NETWORK
        return BroadcastCVNetwork(cvNumber, lockToBroadcast);
    #else

        kernelConditionTableLock->Acquire();
        kernelLockTableLock->Acquire();
        /* user prog trying to request a ConditionVar number out of range */
        if(cvNumber < 0 || cvNumber > MAX_CONDITIONS-1)
        {
           // printf("ERROR: Condition var number is out of bounds. Max condition vars allowed %d\n.", MAX_CONDITIONS);
            kernelConditionTableLock->Release();
            kernelLockTableLock->Release();
            return -1;
        }
        /* user prog trying to signal a lock that is out of range */
        if(lockToBroadcast < 0 || lockToBroadcast > MAX_LOCKS-1)
        {
            printf("ERROR: Lock number is out of bounds. Max lock allowed %d\n.", MAX_LOCKS);
            kernelConditionTableLock->Release();
            kernelLockTableLock->Release();
            return -1;
        }
        // check address spaces for both lock and cv
        if(kernelLockTable[lockToBroadcast]->space != currentThread->space ||
           kernelConditionTable[cvNumber]->space != currentThread->space)
        {
            printf("ERROR: Trying to access a lock or condition var that is not part of thread's address space.\n");
            kernelConditionTableLock->Release();
            kernelLockTableLock->Release();
            return -1;
        }
        /* check if the cv is deleted */
        if(kernelConditionTable[cvNumber]->isDeleted)
        {
            printf("ERROR: %s was destroyed. Cannot be signalled.\n", kernelConditionTable[cvNumber]->condition->getName());
            kernelConditionTableLock->Release();
            kernelLockTableLock->Release();
            return -1;
        }
        /* check if the lock is deleted */
        if(kernelLockTable[lockToBroadcast]->isDeleted)
        {
            printf("ERROR: %s was destroyed. Cannot be signalled.\n", kernelLockTable[lockToBroadcast]->lock->getName());
            kernelConditionTableLock->Release();
            kernelLockTableLock->Release();
            return -1;
        }
        // if the cv was supposed to be deleted
        if(kernelConditionTable[cvNumber]->isToBeDeleted)
        {
            kernelConditionTable[cvNumber]->condition->Broadcast(kernelLockTable[lockToBroadcast]->lock);
            kernelConditionTable[cvNumber]->condition->~Condition();
            kernelConditionTable[cvNumber]->isDeleted = true;
            kernelConditionTable[cvNumber]->isToBeDeleted = false;
            kernelConditionTableLock->Release();
            kernelLockTableLock->Release();
            return 1;
        }
        //printf("%s Broadcast.\n", kernelConditionTable[cvNumber]->condition->getName());
        kernelConditionTable[cvNumber]->condition->Broadcast(kernelLockTable[lockToBroadcast]->lock);
        kernelConditionTableLock->Release();
        kernelLockTableLock->Release();
        return 1;

    #endif
}

#ifdef NETWORK
int DestroyCVNetwork(int cvNumber) {
    int myMailBox = currentThread->getID();

    kernelOutPktHdr.to = serverMachineId;           //servers machine ID
    kernelOutMailHdr.to = serverId;                 // server's mailbox
    kernelOutMailHdr.from = myMailBox; // my mailbox

    sprintf(kernelSendData, "%i,%i", SC_DestroyCV, cvNumber);
    kernelOutMailHdr.length = strlen(kernelSendData) + 1;

    // send the package
    PostSuccess(postOffice->Send(kernelOutPktHdr, kernelOutMailHdr, kernelSendData));

    // receive the response
    postOffice->Receive(myMailBox, &kernelInPktHdr, &kernelInMailHdr, kernelMailBuffer);
    char* token = strtok(kernelMailBuffer, ",");

    if(token != NULL) {
        int retVal = atoi(token);
        if(atoi(token) == 1) {
            return retVal;
        } else {
            printf("ERROR: I got a response but didn't Destroy the CV.\n");
        }
    } else {
        printf("ERROR: Token was null.\n");
    }

    return -1;
}
#endif

// Attempts to destroy a CV
int DestroyCV_Syscall(int cvNumber)
{
    #ifdef NETWORK
        return DestroyCVNetwork(cvNumber);
    #else

        kernelConditionTableLock->Acquire();
        // check if the cv is out of range
        if(cvNumber < 0 || cvNumber > MAX_CONDITIONS-1)
        {
            printf("ERROR: Condition var number is out of bounds. Max condition vars allowed %d.\n", MAX_CONDITIONS);
            kernelConditionTableLock->Release();
            return -1;
        }
        // check if the condition has already been deleted
        if(kernelConditionTable[cvNumber]->isDeleted)
        {
            printf("ERROR: Condition has already been destroyed.\n");
            kernelLockTableLock->Release();
            return -1;
        }
        // check if the CV is in use
        if(!kernelConditionTable[cvNumber]->condition->WaitingQueueIsEmpty())
        {
            printf("Cannot delete the condition var. %s var has waiting threads. Marking it to be deleted.\n", kernelConditionTable[cvNumber]->condition->getName());
            kernelConditionTable[cvNumber]->isToBeDeleted = true;
            kernelConditionTableLock->Release();
            return -1;
        }
        if(!kernelConditionTable[cvNumber])
        {
            printf("ERROR: Condition was never created.");
            kernelConditionTableLock->Release();
            return -1;
        }
        if(kernelConditionTable[cvNumber]->space != currentThread->space)
        {
            printf("ERROR: Lock is not part of process's addr space.\n");
            kernelLockTableLock->Release();
            return -1;
        }

        kernelConditionTable[cvNumber]->condition->~Condition();
        kernelConditionTable[cvNumber]->isDeleted = true;
        amtOfCVs--;
        kernelConditionTableLock->Release();
        return 1;
    #endif
}

void kernelThread_func(unsigned int funcAddr)
{
	processTableLock->Acquire();
	// set nachos registers
	machine->WriteRegister(PCReg, funcAddr);
	machine->WriteRegister(NextPCReg, machine->ReadRegister(PCReg)+4);
	//printf("Writing PC Reg stuff for %s\n", currentThread->getName());
	// call restore state
	currentThread->space->RestoreState();
	//printf("Restoring State for %s\n", currentThread->getName());
	// calculate stack register - call for the addrspace to set the stackLock
	// int field to the location for the stack and write this to the machine 
	// StackReg register
	// ask address space for 8 unused stack pages
	// AddrSpace* currentProcessSpace = currentThread->space;
	//unsigned int stackAddr = currentProcessSpace->GiveNewStackSpace(currentThread);
	machine->WriteRegister(StackReg, currentThread->stackSpaceAddr);
	int stackPage = currentThread->stackSpaceAddr/PageSize;
	/*iptLock->Acquire();
	for(int i=stackPage; i>(stackPage - PagesPerStack); i--) {
		mmu->iptSetEntry(mmu->iptGetSize(), currentThread->space->FindTranslationEntry(i), currentThread->space->GetProcessId());
	}
	iptLock->Release();*/
	//printf("Stack stuff for Forking Thread %s\n", currentThread->getName());
	processTableLock->Release();
	machine->Run();
}

// Forks a new thread to perform operations defined in the given function
void Fork_Syscall(unsigned int addrOfFunction, unsigned int addrOfThreadName, int charLength)
{
	processTableLock->Acquire();
	DEBUG('s', "Fork syscall made!\n");
	//printf("Fork system call made!\n");
	//printf("Current threads in this process: %d\n", processTable[currentThread->space->GetProcessId()]->currentThreadCount);
	// check this process's thread count
	if(processTable[currentThread->space->GetProcessId()]->currentThreadCount == MaxUserThreads)
	{
		printf("ERROR: Process has launched the maximum amount of threads.\n");
		processTableLock->Release();
		return;
	}
	// add to this process's thread count
	//processTable[currentThread->space->GetProcessId()]->currentThreadCount++;
	char* threadName = new char[charLength+1];
	if(!threadName)
	{
		processTableLock->Release();
		printf("ERROR: Problem creating thread name.\n");
		return;
	}
	// check if the name addr passed in is a bad virtual addr
	if(copyin(addrOfThreadName,charLength, threadName) == -1)
	{
		printf("ERROR: Bad pointer for thread name.\n");
		delete threadName;
		processTableLock->Release();
		return;
	}
	threadName[charLength] = '\0';
	//printf("Creating new Thread with name: %s\n", threadName);
	Thread* newThread = new Thread(threadName);

	newThread->isDeleted= false;
	/* set the new thread's addr space to the current process's addr space */
	newThread->space = currentThread->space;
	//processTableLock->Release();
	/* store the stack space addr */
	newThread->stackSpaceAddr = currentThread->space->GiveNewStackSpace(newThread);
	//printf("Giving Thread %s stack space.\n", threadName);
	/* fork to the kernelThreadFunc and set nachos registers */
	newThread->Fork((VoidFunctionPtr)kernelThread_func, addrOfFunction);
	processTableLock->Release();
}

void Exec_Thread(int temp)
{
	//printf("Exec thread method forked.\n");
	machine->WriteRegister(PCReg, 0);
	machine->WriteRegister(NextPCReg, machine->ReadRegister(PCReg)+4);
	currentThread->space->InitRegisters();
	currentThread->space->RestoreState();
	// add this thread to the process table
	for(int i=0; i < MaxUserThreads; i++)
	{
		if(processTable[currentThread->space->GetProcessId()]->threads[i] == NULL || 
		   processTable[currentThread->space->GetProcessId()]->threads[i]->isDeleted)
		{
			processTable[currentThread->space->GetProcessId()]->threads[i] = currentThread;
			break;
		}
	}
	processTableLock->Release();
	//machine->WriteRegister(StackReg, currentThread->stackSpaceAddr);
	machine->Run();
}

// Execute a new process
void Exec_Syscall(unsigned int addrPathToFile, int lengthOfPath)
{
	processTableLock->Acquire();
	char* pathToFile = new char[lengthOfPath+1];
	if(!pathToFile)
	{
		processTableLock->Release();
		return;
	}
	// check if the name addr passed in is a bad virtual addr
	if(copyin(addrPathToFile,lengthOfPath, pathToFile) == -1)
	{
		printf("ERROR: Bad pointer for process filename.\n");
		processTableLock->Release();
		delete pathToFile;
		return;
	}
	OpenFile* executable = fileSystem->Open(pathToFile);
	AddrSpace* space;
	if (executable == NULL) {
		printf("Unable to open file %s\n", pathToFile);
		processTableLock->Release();
		return;
    }
	if(currentProcessesRunning == MAX_PROCESSES-1)
	{
		printf("ERROR: Max process limit was reached. Please wait until other user programs are finished.\n");
		processTableLock->Release();
		return;
	}
	
	// construct new addrspace
	space = new AddrSpace(executable);
	if (space == NULL)
	{
		printf("ERROR: Max process limit was reached. Please wait until other user programs are finished.\n");
		processTableLock->Release();
		return;
	}
	Thread* newThread = new Thread(pathToFile);
	newThread->isDeleted = false;
	newThread->space = space;
	newThread->Fork((VoidFunctionPtr) Exec_Thread, 0);
}

// Try to exit the process. If last thread then interrupt->Halt to
// stop nachos
void Exit_Syscall(int exitArg)
{
	//printf("Exit Syscall\n");
	//currentThread->Finish();	
	processTableLock->Acquire();
	//printf("Current thread count %d\n", processTable[currentThread->space->GetProcessId()]->currentThreadCount);
	//printf("Active thread count %d\n", processTable[currentThread->space->GetProcessId()]->activeThreadCount);
	int threadToDestroy = -1;
	// determine which case we are dealing with
	/******* if last thread in last process *******/
	if(currentProcessesRunning == 1 && 
	   processTable[currentThread->space->GetProcessId()]->activeThreadCount == 1 &&
	   processTable[currentThread->space->GetProcessId()]->currentThreadCount == 1)
	{
		printf("STATE 1 - CALLING SYSTEM HALT\n");
		DEBUG('s', "*** Exit State: Last process running and last thread running. Halt called.\n");
		processTableLock->Release();
		if(exitArg != -1) {
			printf("Exit Result: %d \n", exitArg);
		}
		interrupt->Halt();
		return;
	}
	
	/******* if last executing thread in process but not the last process *******/
	if(currentProcessesRunning != 1 
	   && processTable[currentThread->space->GetProcessId()]->activeThreadCount == 1 &&
	   processTable[currentThread->space->GetProcessId()]->currentThreadCount == 1)
	{
		//printf("STATE 2 - Thread: %s is the last thread in process #%d.\n", currentThread->getName(), currentThread->space->GetProcessId());
		DEBUG('s', "*** Exit State: Last executing thread in the process but not the last process.\n");
		// release all memory for this process
		currentThread->space->ReleaseAllMem();
		// delete address space
		currentThread->space->~AddrSpace();
		// remove the process table entry
		processTable[currentThread->space->GetProcessId()]->isDeleted = true;
		processTable[currentThread->space->GetProcessId()] = NULL;
		currentProcessesRunning--;
		processTableLock->Release();
		if(exitArg != -1) {
			printf("Exit Result: %d \n", exitArg);
		}
		currentThread->Finish();
		return;
	}
	
	/******************* not last thread in a process *****************/
	//printf("STATE 3 - Deleting thread: %s from process #%d.\n", currentThread->getName(), currentThread->space->GetProcessId());
	DEBUG('s', "\n------- Threads in process %d ------\n", currentThread->space->GetProcessId());
	DEBUG('s', "-----------------------------------------\n");
	for(int i=0; i < MaxUserThreads; i++)
	{
		if(processTable[currentThread->space->GetProcessId()]->threads[i] != NULL)
		{
			if(!processTable[currentThread->space->GetProcessId()]->threads[i]->isDeleted)
			{
				DEBUG('s', "Thread #%d: %s State: %d\n",i, processTable[currentThread->space->GetProcessId()]->threads[i]->getName(), processTable[currentThread->space->GetProcessId()]->threads[i]->getStatus());
			}
		}
	}
	DEBUG('s', "--------------------------------------------\n");
	for(int i=0; i < MaxUserThreads; i++)
	{
		if(processTable[currentThread->space->GetProcessId()]->threads[i] != NULL
			&& processTable[currentThread->space->GetProcessId()]->threads[i] == currentThread)
		{
			threadToDestroy = i;
			DEBUG('s', "Thread to get rid of: %s ***\n", processTable[currentThread->space->GetProcessId()]->threads[i]->getName());
			break;
		}
	}
	if(threadToDestroy == -1)
	{
		printf("FATAL ERROR: %s called Exit() but cannot find this thread in the process's thread table.\n", currentThread->getName());
		processTableLock->Release();
		return;
	}
	// release 8 pages of memory
	currentThread->space->ReleaseStackMem(threadToDestroy);
	// update the processtable 
	processTable[currentThread->space->GetProcessId()]->currentThreadCount--;
	processTable[currentThread->space->GetProcessId()]->activeThreadCount--;
	processTable[currentThread->space->GetProcessId()]->threads[threadToDestroy]->isDeleted = true;
	processTable[currentThread->space->GetProcessId()]->threads[threadToDestroy] = NULL;
	// print the remaining threads that are in process table for this process
	DEBUG('s', "\n--- Remaining threads in this process ---\n");
	DEBUG('s', "-----------------------------------------\n");
	for(int i=0; i < MaxUserThreads; i++)
	{
		if(processTable[currentThread->space->GetProcessId()]->threads[i] != NULL)
		{
			if(!processTable[currentThread->space->GetProcessId()]->threads[i]->isDeleted)
			{
				DEBUG('s', "Thread #%d: %s State: %d\n",i, processTable[currentThread->space->GetProcessId()]->threads[i]->getName(), processTable[currentThread->space->GetProcessId()]->threads[i]->getStatus());
			}
		}
	}
	DEBUG('s', "-----------------------------------------\n");
	processTableLock->Release();
	//scheduler->ReadyToRun(processTable[currentThread->space->GetProcessId()]->threads[threadToDestroy-1]);
	if(exitArg != -1) {
		printf("Exit Result: %d \n", exitArg);
	}
	currentThread->Finish();
}

void SWrite_Syscall(unsigned int vaddr, int len, int value1, int value2)
{
	char *buf;		// Kernel buffer for output
    OpenFile *f;	// Open file for output
    
    if ( !(buf = new char[len]) ) {
		printf("%s","Error allocating kernel buffer for write!\n");
		return;
    } else {
        if ( copyin(vaddr,len,buf) == -1 ) {
			printf("%s","Bad pointer passed to to write: data not written\n");
			delete[] buf;
			return;
		}
    }
	
	int foundDec = 0;
	for (int ii=0; ii<len; ii++) 
	{
		if(buf[ii] == '[')
		{
			if(foundDec == 0)
			{
				printf("[%d]", value1);
				foundDec++;
				ii += 3;
				continue;
			}
			else
			{
				printf("[%d]", value2);
				ii += 3;
				continue;
			}
		}
		printf("%c",buf[ii]);
	}
	delete[] buf;
}
void ToString_Syscall(unsigned int vaddr, int len, int number) {
	char *buf;		// Kernel buffer for output
    OpenFile *f;	// Open file for output
    
    if ( !(buf = new char[len]) ) {
		printf("%s","Error allocating kernel buffer for int to string conversion!\n");
		return;
    } else {
        if ( copyin(vaddr,len,buf) == -1 ) {
			printf("%s","Bad pointer passed to ToString: data not written\n");
			delete[] buf;
			return;
		}
    }
	sprintf(buf, "%d", number);
	copyout(vaddr, len, buf);
	
    delete[] buf;
}

int Append_Syscall(unsigned int vaddrA, unsigned int vaddrB, unsigned int vaddrS) {
	
	// Kernel buffers for output
	char *bufA;
	char *bufB;
	int len = 3;
    OpenFile *f;	// Open file for output
	
	int *sizes = new int[len];
	/*
	 * sizes[0] is the maximum size of the array being appended TO
	 * sizes[1] is the number of characters in the first array
	 * sizes[2] is the number of characters in the second array
	 */
	
	// just like copyin() but for integer arrays instead of character arrays
	bool result;
    int n=0;			// The number of bytes copied in
    int *paddr = new int;
	
    while ( n >= 0 && n < len) {
		result = machine->ReadMem( vaddrS, 4, paddr );
		while(!result) // FALL 09 CHANGES
		{
   			result = machine->ReadMem( vaddrS, 4, paddr ); // FALL 09 CHANGES: TO HANDLE PAGE FAULT IN THE ReadMem SYS CALL
		}	
		
		sizes[n++] = *paddr;
		
		if ( !result ) {
			//translation failed
			return -1;
		}
		
		vaddrS+= 4;
    }
	
    delete paddr;
    if(len == -1) {
		printf("%s", "Bad pointer passed to Append: data not written.\n");
		return -1;
	}
	
	len = sizes[1] + sizes[2];
	
	if(len > sizes[0]) {
		printf("%s","The size of the array being modified is too small for this action.\n");
		return -1;
	}
    if ( !(bufA = new char[sizes[0]]) || !(bufB = new char[sizes[2]]) ) {
		printf("%s","Error allocating kernel buffer for string concatenation!\n");
		return -1;
    } else {
        if ( copyin(vaddrA,sizes[1],bufA) == -1 || copyin(vaddrB,sizes[2],bufB) == -1 ) {
			printf("%s","Bad pointer passed to Append: data not written.\n");
			delete[] bufA;
			delete[] bufB;
			delete[] sizes;
			return -1;
		}
    }
	int jj = 0;
	for (int ii=sizes[1]; ii<len; ii++) {
		bufA[ii] = bufB[jj];
		jj++;
	}
	copyout(vaddrA, len, bufA);
	
    delete[] bufA;
	delete[] bufB;
	delete[] sizes;
	
	return len;
}

void WriteAll_Syscall(unsigned int vaddrA, unsigned int vaddrSizes, unsigned int vaddrInputs) {
	// Kernel buffer for output
	char *bufA;
	int *inputs; // integer array of inputs
	int sizesLen = 2;
    int *sizes; 
	/* sizes: integer array of lengths
	 * sizes[0] is the number of characters in the first array (vaddrA)
	 * sizes[1] is the size of the input array (vaddrInputs)
	 */
	if(!(sizes = new int[sizesLen])) {
		printf("%s","Error allocating kernel buffer for WriteAppend: data not written\n");
		return;
	}
	if ( copyinInt(vaddrSizes,sizesLen,sizes) == -1 ) {
		printf("%s","Bad size array pointer passed to WriteAppend: data not written\n");
		delete[] sizes;
		return;
    }
	else {
		for(int i=0; i<sizesLen; i++) {
			if(!sizes[0]) {
				printf("%s","Bad size array pointer passed to WriteAppend: data not written\n");
				delete[] sizes;
				return;
			}
		}
	}
	
	if(!(bufA = new char[sizes[0]])) {
		printf("%s","Error allocating kernel buffer for WriteAppend: data not written\n");
		delete[] sizes;
		return;
	}
	if ( copyin(vaddrA,sizes[0],bufA) == -1 ) {
		printf("%s","Bad character array pointer passed to WriteAppend: data not written\n");
		delete[] sizes;
		delete[] bufA;
		return;
    }
	
	if(sizes[1] > 0) {
		if(!(inputs = new int[sizes[1]])) {
			printf("%s","Error allocating kernel buffer for WriteAppend: data not written\n");
			delete[] sizes;
			delete[] bufA;
			return;
		}
		else {
			if(copyinInt(vaddrInputs, sizes[1], inputs) == -1) {
				printf("%s","Bad input array pointer passed to WriteAppend: data not written\n");
				delete[] sizes;
				delete[] bufA;
				delete[] inputs;
				return;
			}
		}
	}

	int inputIndex = 0;
	IntStatus oldLevel = interrupt->SetLevel(IntOff);
	for (int ii=0; ii<sizes[0]; ii++) {
		if(bufA[ii] == '[') {
			printf("[%d]", inputs[inputIndex]);
			inputIndex++;
			ii += 3;
			continue;
		}
		printf("%c",bufA[ii]);
	}
	(void) interrupt->SetLevel(oldLevel);
	
	delete[] sizes;
	delete[] bufA;
	if(inputs) {
		delete[] inputs;
	}
}

int Rand_Syscall(int min, int max)
{
	static int Init = 0;
	int rc;
	
	if (Init == 0)
	{
		/*
		 *  As Init is static, it will remember it's value between
		 *  function calls.  We only want srand() run once, so this
		 *  is a simple way to ensure that happens.
		 */
		srand(time(NULL));
		Init = 1;
	}
	
	/*
	 * Formula:  
	 *    rand() % N   <- To get a number between 0 - N-1
	 *    Then add the result to min, giving you 
	 *    a random number between min - max.
	 */  
	rc = (rand() % (max - min + 1) + min);
	
	return rc;
}

#ifdef NETWORK

int CreateIntMV_Syscall(unsigned int nameAddress, int lengthOfName, int sizeOfArray) {
    char *mvName;      // Kernel buffer for holding the name
    if ( !(mvName = new char[lengthOfName]) ) {
        printf("%s","Error allocating kernel buffer CreateIntMV!\n");
        return -1;
    } else {
        if ( copyin(nameAddress,lengthOfName,mvName) == -1 ) {
            printf("%s","Bad pointer passed to ToString: data not written\n");
            delete[] mvName;
            return -1;
        }
    }

    int myMailBox = currentThread->getID();

    // setup the message traffic details
    kernelOutPktHdr.to = serverMachineId;           //servers machine ID
    kernelOutMailHdr.to = serverId;                 // server's mailbox
    kernelOutMailHdr.from = myMailBox; // my mailbox

    // create the package to send
    sprintf(kernelSendData, "%i,%s,%i", SC_CreateIntMV, mvName, sizeOfArray);
    kernelOutMailHdr.length = strlen(kernelSendData) + 1;

    // send the package
    PostSuccess(postOffice->Send(kernelOutPktHdr, kernelOutMailHdr, kernelSendData));

    // wait for the receive
    postOffice->Receive(myMailBox, &kernelInPktHdr, &kernelInMailHdr, kernelMailBuffer);
    char* token = strtok(kernelMailBuffer, ",");
    if(token != NULL) {
        if(atoi(token) == 1) {
            // we have been signaled successfully
            token = strtok(NULL, ",");
            if(token) {
                return atoi(token);
            } else {
                return -1;
            }
        } else {
            printf("ERROR: I got a response but didn't make the Int MV.\n");
        }
    } else {
        printf("ERROR: Token was null.\n");
    }

    return -1;
}

int AlterIntMV_syscall(int mvKey, int index, int newValue) {
    int myMailBox = currentThread->getID();

    kernelOutPktHdr.to = serverMachineId; //servers machine ID
    kernelOutMailHdr.to = serverId; // server's mailbox
    kernelOutMailHdr.from = myMailBox; // my mailbox

    sprintf(kernelSendData, "%i,%i,%i,%i", SC_AlterIntMV, mvKey, index, newValue);
    kernelOutMailHdr.length = strlen(kernelSendData) + 1;

    PostSuccess(postOffice->Send(kernelOutPktHdr, kernelOutMailHdr, kernelSendData));

    postOffice->Receive(myMailBox, &kernelInPktHdr, &kernelInMailHdr, kernelMailBuffer);
    char* token = strtok(kernelMailBuffer, ",");
    if(token != NULL) {
        if(atoi(token) == 1) {
            // we have been signaled sucsessfully
            token = strtok(NULL, ",");
            if(token) {
                return atoi(token);
            } else {
                return BAD_VALUE_SENTINAL;
            }
        } else {
            printf("ERROR: I got a response but didn't make the Int MV Change.\n");
        }
    } else {
        printf("ERROR: Token was null.\n");
    }

    return BAD_VALUE_SENTINAL;
}

int RetrieveIntMV_Syscall(int mvKey, int index) {
    int myMailBox = currentThread->getID();

    kernelOutPktHdr.to = serverMachineId; //servers machine ID
    kernelOutMailHdr.to = serverId; // server's mailbox
    kernelOutMailHdr.from = myMailBox; // my mailbox

    sprintf(kernelSendData, "%i,%i,%i", SC_RetrieveIntMV, mvKey, index);
    kernelOutMailHdr.length = strlen(kernelSendData) + 1;

    PostSuccess(postOffice->Send(kernelOutPktHdr, kernelOutMailHdr, kernelSendData));

    postOffice->Receive(myMailBox, &kernelInPktHdr, &kernelInMailHdr, kernelMailBuffer);
    char* token = strtok(kernelMailBuffer, ",");
    if(token != NULL) {
        if(atoi(token) == 1) {
            // we have been signaled sucsessfully
            token = strtok(NULL, ",");
            if(token) {
                return atoi(token);
            } else {
                return BAD_VALUE_SENTINAL;
            }
        } else {
            printf("ERROR: I got a response but didn't make the Int MV Change.\n");
        }
    } else {
        printf("ERROR: Token was null.\n");
    }

    return BAD_VALUE_SENTINAL;
}

void DestroyIntMV_syscall(unsigned int nameAddress, int lengthOfName) {
    printf("Destroy Int MV - Currently not Supported.\n");
}
#endif

void printTLB() {
	// the TLB entry about to be replaced is not
	IntStatus oldLevel = interrupt->SetLevel(IntOff);
	for(int i=0; i<TLBSize; i++) {
	printf("TLB: ");
	printf("VPN[%d] ", machine->tlb[i].virtualPage);
	printf("PPN[%d] ", machine->tlb[i].physicalPage);
	printf("Valid[%d] ", machine->tlb[i].valid);
	printf("Dirty[%d]\n", machine->tlb[i].dirty);
	}
	(void) interrupt->SetLevel(oldLevel);
}

void updateTLB(VMTranslationEntry* update) {
	// the TLB entry about to be replaced is not
	//IntStatus oldLevel = interrupt->SetLevel(IntOff);
		
	// dirty bit propagation	
	if(machine->tlb[currentTLB].valid) {
		mmu->iptSetDirty(machine->tlb[currentTLB].physicalPage, machine->tlb[currentTLB].dirty);
	}
		
	machine->tlb[currentTLB].virtualPage = update->virtualPage;
	machine->tlb[currentTLB].physicalPage = update->physicalPage;
	machine->tlb[currentTLB].valid = update->valid;
	machine->tlb[currentTLB].use = update->use;
	machine->tlb[currentTLB].dirty = update->dirty;
	//printf("On Update Dirty[%d]\n", machine->tlb[currentTLB].dirty);
	machine->tlb[currentTLB].readOnly = update->readOnly;
	
	// update current TLB index
	currentTLB = (currentTLB+1) % TLBSize;
	//(void) interrupt->SetLevel(oldLevel);
}

void HandlePageFault(unsigned int badvaddr) {
	//IntStatus oldLevel = interrupt->SetLevel(IntOff);
	int i;
	int index = -1;
	int availablePageInPhysMem;
	int vpn = (int)badvaddr/PageSize;
	int processID = currentThread->space->GetProcessId();
	VMTranslationEntry* translation;
	VMTranslationEntry* pageToEvict;
	
	// propagate dirty bits from TLB to IPT
	IntStatus oldLevel = interrupt->SetLevel(IntOff);
	//printf("Propagating dirty bits.\n");
	for(i=0; i< TLBSize; i++) {
		if(machine->tlb[i].valid == TRUE) {
			mmu->iptSetDirty(machine->tlb[i].physicalPage, machine->tlb[i].dirty);
		}
	}
	//(void) interrupt->SetLevel(oldLevel);
	
	
	#ifdef NETWORK
        // pick a random server
        serverMachineId = rand()%NUMBER_OF_SERVERS;
    #endif

	// check for the virtual address in the ipt
	//iptLock->Acquire();
	//printf("Searching for VPN[%d]\n", vpn);
	index = mmu->iptFind(vpn, processID);
	if(index == -1) {
		// the vpn requested IS NOT in memory
		DEBUG('p', "IPT Miss for VPN[%d]!\n", vpn);
		// printf("IPT Miss (VPN[%d] requested).\n", vpn);
		
		// Find a free page in memory
		//memoryBitMapLock->Acquire();
		availablePageInPhysMem = globalPhysicalMemoryBitMap->Find();
		if(availablePageInPhysMem == -1)
		{
			// there is no avaialble free space in phys memory
			//printf("Main Memory is FULL!\n");
			
			// choose page to evict
			pageToEvict = mmu->iptGetEviction();

			DEBUG('p', "Page Eviction at PPN[%d].\n", pageToEvict->physicalPage);
			//printf("Page Eviction at PPN[%d].\n", pageToEvict->physicalPage);
			
			if(pageToEvict->dirty == TRUE) {
				// Find free space in swapfile
				//swapBitMapLock->Acquire();
				pageToEvict->offset = swapfileBitMap->Find();
				pageToEvict->offset = pageToEvict->offset * PageSize;
				pageToEvict->location = SWAPFILE;
				
				if(pageToEvict->offset == -1) {
					printf("ERROR: swapfile is full.\n");
					interrupt->Halt();
					return;
				}
				
				DEBUG('p', "Writing evicted VPN[%d] to SWAPFILE at [%d].\n", pageToEvict->virtualPage, pageToEvict->offset);
				//printf("Writing evicted VPN[%d] to SWAPFILE at [%d] from [%d].\n", pageToEvict->virtualPage, pageToEvict->offset, pageToEvict->physicalPage);
				swapfile->WriteAt(&(machine->mainMemory[pageToEvict->physicalPage*PageSize]), PageSize, (pageToEvict->offset));
				//processTableLock->Acquire();
				//swapBitMapLock->Release();
				
				// update the page table in the process for this particular translation entry
				processTable[pageToEvict->processID]->addrSpace->UpdateTranslation(pageToEvict);
				
				//oldLevel = interrupt->SetLevel(IntOff);
				for(i=0; i< TLBSize; i++) {
					if(machine->tlb[i].physicalPage == pageToEvict->physicalPage && machine->tlb[i].valid) {
						machine->tlb[i].valid = FALSE;
						mmu->iptSetDirty(machine->tlb[i].physicalPage, machine->tlb[i].dirty);
					}
				}
				//(void) interrupt->SetLevel(oldLevel);
				
				//processTable[pageToEvict->processID]->addrSpace->ZeroMemoryPage(pageToEvict->physicalPage);
				processTableLock->Release();
			} // end if (dirty)
			else {
				DEBUG('p', "VPN[%d] evicted but not to swapfile.\n", pageToEvict->virtualPage);
				//printf("VPN[%d] evicted but not to swapfile.\n", pageToEvict->virtualPage);
			}

			// set page number for the page we are trying to bring into main memory
			availablePageInPhysMem = pageToEvict->physicalPage;
		}
		
		// find translation in translation table
		//processTableLock->Acquire();
		translation = processTable[processID]->addrSpace->FindTranslationEntry(vpn);
		//processTableLock->Release();
		
		translation->physicalPage = availablePageInPhysMem;
		// page is in executable
		if(translation->location == EXECUTABLE) {
			DEBUG('p', "Reading VPN[%d] from executable into PPN[%d].\n", vpn, translation->physicalPage);
			//printf("Reading VPN[%d] from executable at [%d] into PPN[%d].\n", vpn, translation->offset, translation->physicalPage);
			// read from executable into memory and update page table
			processTable[processID]->addrSpace->GetPageFromExecutable(translation);
			
		}
		// page is in swapfile
		else if(translation->location == SWAPFILE) {
			//swapBitMapLock->Acquire();
			DEBUG('p', "Reading VPN[%d] from swapfile at[%d] into PPN[%d].\n", vpn, translation->offset, translation->physicalPage);
			//printf("Reading VPN[%d] from swapfile at[%d] into PPN[%d].\n", vpn, translation->offset, translation->physicalPage);
			// read from swapfile into memory and update page table
			swapfile->ReadAt(&(machine->mainMemory[translation->physicalPage*PageSize]), PageSize, (translation->offset));
			
			//printf("HANDLER: Location: %d Offset %d for 434.\n", currentThread->space->pageTable[434].location, currentThread->space->pageTable[434].offset);
			translation->dirty = TRUE;
			//swapfileBitMap->Clear(translation->offset);
					
			//memoryBitMapLock->Release();
			//swapBitMapLock->Release();
		}
		// page is not in executable nor swapfile
		else {
			DEBUG('p', "Reading VPN[%d] from nowhere into PPN[%d] %d.\n", vpn, translation->physicalPage, translation->virtualPage);
			//printf("Reading VPN[%d] from nowhere into PPN[%d].\n", vpn, translation->physicalPage);
			processTable[translation->processID]->addrSpace->ZeroMemoryPage(translation->physicalPage);
			//memoryBitMapLock->Release();
		}
		//memoryBitMapLock->Release();
		translation->valid = TRUE;
		
		// update the page table in the process for this particular translation entry
		//processTableLock->Acquire();
		//maybe use currentThread
		processTable[translation->processID]->addrSpace->UpdateTranslation(translation);
		//processTableLock->Release();
		
		// update IPT
		mmu->iptSetEntry(translation);
		
	}
	else {
		// the vpn requested IS in memory
		DEBUG('p', "IPT Hit.\n");
		//printf("IPT Hit.\n");
		translation = mmu->iptGetEntry(index);
	}
	
	// update TLB with the entry from the ipt
	
	updateTLB(translation);
	//printf("PageFaultException Handled.\n");
	//iptLock->Release();	
	(void) interrupt->SetLevel(oldLevel);
}

void ExceptionHandler(ExceptionType which) {
    int type = machine->ReadRegister(2); // Which syscall?
    int rv=0; 	// the return value from a syscall
	
    if ( which == SyscallException ) {
		switch (type) {
			default:
				DEBUG('a', "Unknown syscall - shutting down.\n");
			case SC_Halt:
				DEBUG('a', "Shutdown, initiated by user program.\n");
				interrupt->Halt();
				break;
			case SC_Create:
				DEBUG('a', "Create syscall.\n");
				Create_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
				break;
			case SC_Open:
				DEBUG('a', "Open syscall.\n");
				rv = Open_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
				break;
			case SC_Write:
				DEBUG('a', "Write syscall.\n");
				Write_Syscall(machine->ReadRegister(4),
							  machine->ReadRegister(5),
							  machine->ReadRegister(6));
				break;
			case SC_SWrite:
				DEBUG('a', "Special Write syscall.\n");
				SWrite_Syscall(machine->ReadRegister(4),
							   machine->ReadRegister(5),
							   machine->ReadRegister(6),
							   machine->ReadRegister(7));
				break;
			case SC_Read:
				DEBUG('a', "Read syscall.\n");
				rv = Read_Syscall(machine->ReadRegister(4),
								  machine->ReadRegister(5),
								  machine->ReadRegister(6));
				break;
			case SC_Close:
				DEBUG('a', "Close syscall.\n");
				Close_Syscall(machine->ReadRegister(4));
				break;
			case SC_Yield:
				DEBUG('a', "Yield syscall.\n");
				Yield_Syscall();
				break;
				/** Lock stuff **/
			case SC_CreateLock:
			    // printf("Creating Lock Syscall.\n");
				DEBUG('a', "CreateLock syscall.\n");
				// pass CreateLock_Syscall the address of the char* and length of char*
				rv = CreateLock_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
				break;
			case SC_Acquire:
				DEBUG('a', "Acquire syscall.\n");
				// pass the lock number to the method to acquire lock
				rv = AcquireLock_Syscall(machine->ReadRegister(4));
				break;
			case SC_Release:
				DEBUG('a', "ReleaseLock syscall.\n");
				rv = ReleaseLock_Syscall(machine->ReadRegister(4));
				break;
			case SC_DestroyLock:
				DEBUG('a', "DestroyLock syscall.\n");
				rv = DestroyLock_Syscall(machine->ReadRegister(4));
				break;
				/** Condition Var stuff **/
			case SC_CreateCV:
				DEBUG('a', "CreateCV syscall.\n");
				// pass CreateCV_Syscall the address of the char* and length of char*
				rv = CreateCV_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
				break;
			case SC_Wait:
				DEBUG('a', "Wait syscall.\n");
				// pass the index for the cv and lock to wait on
				rv = WaitCV_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
				break;
			case SC_Signal:
				DEBUG('a', "Signal syscall.\n");
				// pass the index for the lock to signal
				rv = SignalCV_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
				break;
			case SC_Broadcast:
				DEBUG('a', "Broadcast syscall.\n");
				rv = BroadcastCV_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
				break;
			case SC_DestroyCV:
				DEBUG('a', "DestroyCV syscall.\n");
				rv = DestroyCV_Syscall(machine->ReadRegister(4));
				break;
			case SC_Fork:
				DEBUG('a', "Fork syscall.\n");
				Fork_Syscall(machine->ReadRegister(4), machine->ReadRegister(5), machine->ReadRegister(6));
				break;
			case SC_Exec:
				DEBUG('a', "Exec syscall.\n");
				Exec_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
				break;
			case SC_Exit:
				DEBUG('a', "Exit syscall.\n");
				Exit_Syscall(machine->ReadRegister(4));
				break;
			case SC_ToString:
				DEBUG('a', "ToString syscall.\n");
				ToString_Syscall(machine->ReadRegister(4), machine->ReadRegister(5), machine->ReadRegister(6));
				break;
			case SC_Append:
				DEBUG('a', "Append syscall.\n");
				rv = Append_Syscall(machine->ReadRegister(4), machine->ReadRegister(5), machine->ReadRegister(6));
				break;
			case SC_Rand:
				DEBUG('a', "Rand syscall.\n");
				rv = Rand_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
				break;
            #ifdef NETWORK
			case SC_CreateIntMV:
			    DEBUG('a', "CreateIntMV syscall.\n");
			    rv = CreateIntMV_Syscall(machine->ReadRegister(4),
			                             machine->ReadRegister(5),
			                             machine->ReadRegister(6));
			    break;
            case SC_RetrieveIntMV:
                DEBUG('a', "RetrieveIntMV syscall.\n");
                rv = RetrieveIntMV_Syscall(machine->ReadRegister(4),
                                           machine->ReadRegister(5));
                break;
            case SC_AlterIntMV:
                DEBUG('a', "AlterIntMV syscall.\n");
                rv = AlterIntMV_syscall(machine->ReadRegister(4),
                                        machine->ReadRegister(5),
                                        machine->ReadRegister(6));
                break;
            case SC_DestroyIntMV:
                DEBUG('a', "DestroyIntMV syscall.\n");
                DestroyIntMV_syscall(machine->ReadRegister(4),
                                     machine->ReadRegister(5));
                break;
            #endif
			case SC_WriteAll:
				DEBUG('a', "Write All syscall.\n");
				WriteAll_Syscall(machine->ReadRegister(4),
							   machine->ReadRegister(5),
							   machine->ReadRegister(6));
				break;
		}
		
		// Put in the return value and increment the PC
		machine->WriteRegister(2,rv);
		machine->WriteRegister(PrevPCReg,machine->ReadRegister(PCReg));
		machine->WriteRegister(PCReg,machine->ReadRegister(NextPCReg));
		machine->WriteRegister(NextPCReg,machine->ReadRegister(PCReg)+4);
		return;
    } else if( which == PageFaultException ){
		DEBUG('m', "PageFaultException Caught.\n");
		
		//printf("VAddr[%d] VPN[%d] || ", machine->ReadRegister(39), (machine->ReadRegister(39)/PageSize));
		//printTLB();
		HandlePageFault(machine->ReadRegister(39));
		//printTLB();
		return;

    } else {
		cout<<"Unexpected user mode exception - which:"<<which<<"  type:"<< type<<endl;
		cout<<"type/PageSize:"<<(type/PageSize)<<endl;
		interrupt->Halt();
    }
}
